use bcrypt::{hash, verify, DEFAULT_COST};
use chrono::Utc;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use sqlx::{MySqlPool, Row};

use crate::models::{User, RegisterRequest, LoginRequest, LoginResponse, UserResponse, ApiResponse};
use tracing::{info, error, debug};

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    pub sub: String, // 用户ID
    pub exp: usize,  // 过期时间
    pub iat: usize,  // 签发时间
}

#[derive(Clone)]
pub struct UserService {
    pool: MySqlPool,
    jwt_secret: String,
}

impl UserService {
    pub fn new(pool: MySqlPool, jwt_secret: String) -> Self {
        Self { pool, jwt_secret }
    }

    pub async fn register(&self, req: RegisterRequest) -> Result<ApiResponse<UserResponse>, Box<dyn std::error::Error>> {
        debug!("开始注册用户: {}", req.username);
        
        // 检查用户名是否已存在
        let existing_user = sqlx::query("SELECT id FROM users WHERE username = ?")
            .bind(&req.username)
            .fetch_optional(&self.pool)
            .await?;

        if existing_user.is_some() {
            error!("用户名已存在: {}", req.username);
            return Ok(ApiResponse::error("用户名已存在"));
        }

        // 密码加密
        let password_hash = hash(req.password.as_bytes(), DEFAULT_COST)?;
        
        let now = Utc::now();

        // 插入新用户
        let user = sqlx::query(r#"
            INSERT INTO users (username, password)
            VALUES (?, ?)
        "#)
        .bind(&req.username)
        .bind(&password_hash)
        .execute(&self.pool)
        .await?;

        let user_id = user.last_insert_id() as i32;
        
        // 获取插入的用户信息
        let user = sqlx::query("SELECT id, username, password FROM users WHERE id = ?")
            .bind(user_id)
            .fetch_one(&self.pool)
            .await?;

        let user = User {
            id: user.try_get::<i32, _>("id")?,
            username: user.try_get::<String, _>("username")?,
            password: user.try_get::<String, _>("password")?,
        };

        info!("用户注册成功: {} (ID: {})", user.username, user.id);
        Ok(ApiResponse::success(user.into(), "注册成功"))
    }

    pub async fn login(&self, req: LoginRequest) -> Result<ApiResponse<LoginResponse>, Box<dyn std::error::Error>> {
        // 查找用户
        let user = sqlx::query("SELECT id, username, password FROM users WHERE username = ?")
            .bind(&req.username)
            .fetch_optional(&self.pool)
            .await?;

        let user = match user {
            Some(row) => {
                User {
                    id: row.try_get::<i32, _>("id")?,
                    username: row.try_get::<String, _>("username")?,
                    password: row.try_get::<String, _>("password")?,
                }
            }
            None => return Ok(ApiResponse::error("用户名或密码错误")),
        };

        // 验证密码
        if !verify(req.password.as_bytes(), &user.password)? {
            return Ok(ApiResponse::error("用户名或密码错误"));
        }

        // 生成JWT token
        let token = self.generate_token(user.id)?;

        let login_response = LoginResponse {
            token,
            user: user.into(),
        };

        Ok(ApiResponse::success(login_response, "登录成功"))
    }

    pub async fn get_user_by_id(&self, user_id: i32) -> Result<Option<User>, Box<dyn std::error::Error>> {
        let user = sqlx::query("SELECT id, username, password FROM users WHERE id = ?")
            .bind(user_id)
            .fetch_optional(&self.pool)
            .await?;

        match user {
            Some(row) => {
                let user = User {
                    id: row.try_get::<i32, _>("id")?,
                    username: row.try_get::<String, _>("username")?,
                    password: row.try_get::<String, _>("password")?,
                };
                Ok(Some(user))
            }
            None => Ok(None),
        }
    }

    fn generate_token(&self, user_id: i32) -> Result<String, Box<dyn std::error::Error>> {
        let now = Utc::now();
        let exp = (now + chrono::Duration::hours(24)).timestamp() as usize;
        let iat = now.timestamp() as usize;

        let claims = Claims {
            sub: user_id.to_string(),
            exp,
            iat,
        };

        let token = encode(
            &Header::default(),
            &claims,
            &EncodingKey::from_secret(self.jwt_secret.as_ref()),
        )?;

        Ok(token)
    }

    pub fn verify_token(&self, token: &str) -> Result<i32, Box<dyn std::error::Error>> {
        let token_data = decode::<Claims>(
            token,
            &DecodingKey::from_secret(self.jwt_secret.as_ref()),
            &Validation::default(),
        )?;

        let user_id = token_data.claims.sub.parse::<i32>()?;
        Ok(user_id)
    }
} 